package com.yuandian.management.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.yuandian.api.management.entity.ConfMpExternalOrganization;
import com.yuandian.api.management.entity.ConfMpInternalOrganization;
import com.yuandian.api.management.vo.ConfMpExternalOrganizationExcelVo;
import com.yuandian.api.management.vo.ConfMpInternalOrganizationExcelVo;
import com.yuandian.api.management.vo.OptionVo;
import com.yuandian.bpm.admin.api.entity.SysDictItem;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.bpm.common.data.resolver.DictResolver;
import com.yuandian.bpm.common.excel.vo.ErrorMessage;
import com.yuandian.management.service.ConfMpExternalOrganizationService;
import com.yuandian.management.service.ConfMpInternalOrganizationService;
import com.yuandian.management.service.ConfMpOrganizationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 说明：中台监控-单位配置service实现
 * @Author zfsn
 * @Date 2023/4/20 13:47
 */
@Service
public class ConfMpOrganizationServiceImpl implements ConfMpOrganizationService {

	@Autowired
	private ConfMpInternalOrganizationService confMpInternalOrganizationService;

	@Autowired
	private ConfMpExternalOrganizationService confMpExternalOrganizationService;

	/**
	 * 查询内部单位选项
	 * @return 通用返回对象
	 */
	@Override
	public R<List<OptionVo>> option() {
		List<ConfMpInternalOrganization> internalOrgList = confMpInternalOrganizationService.lambdaQuery()
				.select(ConfMpInternalOrganization::getOrgCode, ConfMpInternalOrganization::getOrgName)
				.groupBy(ConfMpInternalOrganization::getOrgCode, ConfMpInternalOrganization::getOrgName)
				.list();
		List<OptionVo> list = new ArrayList<>();
		internalOrgList.forEach(org -> {
			OptionVo vo = new OptionVo();
			vo.setLabel(org.getOrgName());
			vo.setValue(org.getOrgCode());
			list.add(vo);
		});
		return R.ok(list);
	}

	/**
	 * 导入内部单位配置
	 * @param excelVoList 导入的数据集
	 * @param bindingResult 用来校验 实体 jsr303 校验失败结果
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<List<ErrorMessage>> importInternalOrganization(List<ConfMpInternalOrganizationExcelVo> excelVoList, BindingResult bindingResult) {
		// 1、校验请求参数
		if (CollUtil.isEmpty(excelVoList)) {
			return R.failed("导入失败，Excel为空或解析失败！");
		}
		// 2、获取单位类型字典
		List<SysDictItem> orgTypeDictItemList = DictResolver.getDictItemsByType("mp_org_type");
		Map<String, String> orgTypeMap = orgTypeDictItemList.stream()
				.collect(Collectors.toMap(SysDictItem::getLabel, SysDictItem::getItemValue, (k1, k2) -> k1));
		// 3、获取内部单位配置唯一性校验集
		Map<String, Integer> uniqueInternalMap = confMpInternalOrganizationService.lambdaQuery()
				.select(ConfMpInternalOrganization::getId,
						ConfMpInternalOrganization::getOrgCode,
						ConfMpInternalOrganization::getDepartmentCode,
						ConfMpInternalOrganization::getAppCode)
				.list()
				.stream()
				.collect(Collectors.toMap(internalOrganization -> internalOrganization.getOrgCode()
						+ "-" + internalOrganization.getDepartmentCode()
						+ "-" + internalOrganization.getAppCode(), ConfMpInternalOrganization::getId, (k1, k2) -> k1));
		// 4、遍历导入的数据集
		long failCount = 0;
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
		assert errorMessageList != null;
		Map<String, ConfMpInternalOrganization> internalOrgMap = new HashMap<>();
		for (ConfMpInternalOrganizationExcelVo excelVo : excelVoList) {
			Set<String> errorMsg = new HashSet<>();
			String internalOrgCode = excelVo.getInternalOrgCode();
			String internalOrgName = excelVo.getInternalOrgName();
			String orgTypeLabel = excelVo.getOrgType();
			String departmentCode = excelVo.getDepartmentCode();
			String departmentName = excelVo.getDepartmentName();
			String appCode = excelVo.getAppCode();
			String appName = excelVo.getAppName();
			// 非空校验：单位编码、单位名称、单位类型、部门编码、部门名称
			if (!StrUtil.isAllNotBlank(internalOrgCode, internalOrgName, orgTypeLabel, departmentCode, departmentName)) {
				errorMsg.add("内部单位非空校验不通过，请检查以下字段：单位编码、单位名称、单位类型、部门编码、部门名称");
				errorMessageList.add(new ErrorMessage(excelVo.getLineNum(), errorMsg));
				failCount++;
				continue;
			}
			// 非法校验：单位类型
			String orgTypeValue = orgTypeMap.get(orgTypeLabel);
			if (StrUtil.isEmpty(orgTypeValue)) {
				errorMsg.add("内部单位非法校验不通过：未知的单位类型，请检查数据字典中是否有对应的单位类型");
				errorMessageList.add(new ErrorMessage(excelVo.getLineNum(), errorMsg));
				failCount++;
				continue;
			}
			ConfMpInternalOrganization.ConfMpInternalOrganizationBuilder builder = ConfMpInternalOrganization.builder();
			// 唯一校验：单位编码 + 部门编码 + 应用编码
			String key = internalOrgCode + "-" + departmentCode + "-" + appCode;
			Integer id = uniqueInternalMap.get(key);
			if (id != null) {
				// 存在重复的编码组合，处理方式是覆盖更新，所以这里创建实体对象时需要回填id
				builder.id(id);
			}
			// 通过校验，创建实体对象添加到待入库集合中，这里用map的作用是相同的key，后面的会覆盖前面的，起到覆盖更新的作用
			internalOrgMap.put(key, builder
					.orgCode(internalOrgCode)
					.orgName(internalOrgName)
					.orgType(orgTypeValue)
					.departmentCode(departmentCode)
					.departmentName(departmentName)
					.appCode(appCode)
					.appName(appName)
					.build());
		}
		// 5、批量写入内部单位配置表
		Collection<ConfMpInternalOrganization> internalOrgList = internalOrgMap.values();
		if (CollUtil.isNotEmpty(internalOrgList)) {
			confMpInternalOrganizationService.saveOrUpdateBatch(internalOrgList, 1000);
		}
		return R.ok(errorMessageList, "导入完成，失败条数：" + failCount);
	}

	/**
	 * 导入外部单位配置
	 * @param excelVoList 导入的数据集
	 * @param bindingResult 用来校验 实体 jsr303 校验失败结果
	 * @return 通用返回对象
	 */
	@Transactional(rollbackFor = Exception.class)
	@Override
	public R<List<ErrorMessage>> importExternalOrganization(List<ConfMpExternalOrganizationExcelVo> excelVoList, BindingResult bindingResult) {
		// 1、校验请求参数
		if (CollUtil.isEmpty(excelVoList)) {
			return R.failed("导入失败，Excel为空或解析失败！");
		}
		// 2、获取外部配置唯一性校验集
		Map<String, Integer> uniqueExternalMap = confMpExternalOrganizationService.lambdaQuery()
				.select(ConfMpExternalOrganization::getId,
						ConfMpExternalOrganization::getOrgCode)
				.list()
				.stream()
				.collect(Collectors.toMap(ConfMpExternalOrganization::getOrgCode,
						ConfMpExternalOrganization::getId, (k1, k2) -> k1));
		// 3、遍历导入的数据集
		long failCount = 0;
		List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
		assert errorMessageList != null;
		Map<String, ConfMpExternalOrganization> externalOrgMap = new HashMap<>();
		for (ConfMpExternalOrganizationExcelVo excelVo : excelVoList) {
			Set<String> errorMsg = new HashSet<>();
			String externalOrgCode = excelVo.getExternalOrgCode();
			String externalOrgName = excelVo.getExternalOrgName();
			// 非空校验：单位编码、单位名称
			if (!StrUtil.isAllNotBlank(externalOrgCode, externalOrgName)) {
				errorMsg.add("外部单位非空校验不通过，请检查以下字段：单位编码、单位名称");
				errorMessageList.add(new ErrorMessage(excelVo.getLineNum(), errorMsg));
				failCount++;
				continue;
			}
			ConfMpExternalOrganization.ConfMpExternalOrganizationBuilder builder = ConfMpExternalOrganization.builder();
			// 唯一校验：单位编码唯一
			Integer id = uniqueExternalMap.get(externalOrgCode);
			if (id != null) {
				// 存在重复的单位编码，处理方式是覆盖更新，所以这里创建实体对象时需要回填id
				builder.id(id);
			}
			// 通过校验，创建实体对象添加到待入库集合中
			externalOrgMap.put(externalOrgCode, builder
					.orgCode(externalOrgCode)
					.orgName(externalOrgName)
					.remarks(excelVo.getDesc())
					.build());
		}
		// 4、批量写入外部单位配置表
		Collection<ConfMpExternalOrganization> externalOrgList = externalOrgMap.values();
		if (CollUtil.isNotEmpty(externalOrgList)) {
			confMpExternalOrganizationService.saveOrUpdateBatch(externalOrgList, 1000);
		}
		return R.ok(errorMessageList, "导入完成，失败条数：" + failCount);
	}
}
